﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Contracts.Control;
using DTO.Projectos;
using DTO.Utilizadores;
using Contracts.DAL;
using DAL;
using DTO;
using System.Collections;
using DTO.Accoes;

namespace Control
{
    public class GestaoProjectos : IGestaoProjectos
    {

        private IRepositorio repositorio = new Repositorio();

        private static Temporizador temporizador = new Temporizador(long.Parse(ResourceConfig.intervalTime));

        private static Dictionary<int, Projecto> projectosEmFasePagamento = new Dictionary<int, Projecto>();

        public GestaoProjectos() {   }

        #region obterProjectos

        private IEnumerable<Projecto> DevolveProjectosNosEstados(ProjectType t, params Estados[] estados) {

            foreach (Estados estado in estados) {
                IEnumerable<Projecto> projectos = repositorio.ObterProjectos(estado, t);
                
                foreach (Projecto p in projectos)
                    yield return p;

            }

            yield break;
        }

        private IEnumerable<Projecto> DevolveProjectosNosEstados(params Estados[] estados) {
            return DevolveProjectosNosEstados(ProjectType.Any, estados);
        }


        public Projecto DevolveProjecto(int idProjecto)
        {
            return repositorio.ObterProjecto(idProjecto);
        }

        public IEnumerable<Projecto> DevolveProjectosParaAnalise()
        {
            return DevolveProjectosNosEstados(Estados.Candidatura);
        }

        public IEnumerable<Projecto> DevolveProjectosParaDespachoAbertura()
        {
            return DevolveProjectosNosEstados(Estados.DespachoAbertura);
        }

        public IEnumerable<Projecto> DevolveProjectosParaParecerTecnico()
        {
            return DevolveProjectosNosEstados(Estados.AnaliseTecnica);
        }

        public IEnumerable<Projecto> DevolveProjectosArquivados()
        {
            return DevolveProjectosNosEstados(Estados.Arquivado);
        }

        public IEnumerable<Projecto> DevolveProjectosDespachoDecisao()
        {
            return DevolveProjectosNosEstados(Estados.DespachoDecisao);
        }

        public IEnumerable<Projecto> DevolveProjectosEmPagamento()
        {
            return DevolveProjectosNosEstados(ProjectType.Incentivos, Estados.Pagamento);
        }

        public IEnumerable<Projecto> DevolveProjectosparaReforçoFinanciamento()
        {
            return DevolveProjectosNosEstados(ProjectType.Incentivos, Estados.Pagamento, Estados.Fechado);
        }

        public IEnumerable<Projecto> DevolveProjectosParaSuspender()
        {
            return DevolveProjectosNosEstados(
                Estados.Candidatura, 
                Estados.DespachoAbertura,
                Estados.AnaliseTecnica,
                Estados.DespachoDecisao,
                Estados.Pagamento);
        }

        public IEnumerable<Projecto> DevolveProjectosParaReactivar()
        {
            return DevolveProjectosNosEstados(Estados.Suspenso);
        }

        public IEnumerable<Projecto> DevolveProjectosParaAlterarDados()
        {
            return DevolveProjectosNosEstados(
                Estados.AnaliseTecnica,
                Estados.DespachoDecisao);
        }

        public IEnumerable<Projecto> DevolveTodosOsProjectos()
        {
            return repositorio.ObterProjectos();
        } 

        #endregion



        public void AdicionaProjecto(Projecto projecto)
        {

            GestaoFase.MarcaEstadoInicial(projecto);
            GestaoAccao.RegistaCandidatura(projecto);
            repositorio.AdicionaProjecto(projecto);
        }

        public void AnaliseProjecto(int idProjecto, bool enquadrado, Tecnico t)
        {
            Projecto p = repositorio.ObterProjecto(idProjecto);
            GestaoAccao.RegistaAnalise(p, enquadrado, t.Nome);
            
            Evento e = enquadrado ? Evento.Favoravel : Evento.Desfavoravel;
            GestaoFase.EvoluiEstado(e, p);

            repositorio.AlteraProjecto(p);
        }

        public void DespachoAbertura(int idProjecto, string despacho, Gestor gestor)
        {
            Projecto p = repositorio.ObterProjecto(idProjecto);
            GestaoAccao.RegistaDespachoAbertura(p, despacho, gestor.Nome);
            GestaoFase.EvoluiEstado(Evento.Favoravel, p);

            repositorio.AlteraProjecto(p);
        }

        public void EmitirParecerTecnico(int idProjecto, string parecer, bool aceite, Gestor gestor)
        {
            Projecto p = repositorio.ObterProjecto(idProjecto);
            GestaoAccao.RegistaParecerTecnico(p, parecer, aceite, gestor.Nome);

            Evento e = aceite ? Evento.Favoravel : Evento.Desfavoravel;
            GestaoFase.EvoluiEstado(Evento.Favoravel, p);

            repositorio.AlteraProjecto(p);
        }

        public void ReenquadrarProjecto(int idProjecto)
        {
            Projecto p = repositorio.ObterProjecto(idProjecto);
            GestaoAccao.RegistaReenquadrarProjecto(p);
            GestaoFase.EvoluiEstado(Evento.Favoravel, p);
            repositorio.AlteraProjecto(p);
        }

        public void DespachoDecisaoIncentivos(int idProjecto, string despacho, bool aceite, double custoElegivel, double montanteFinanciado, DateTime datalimite)
        {
            Incentivos p = repositorio.ObterProjecto(idProjecto) as Incentivos;
            Evento e = aceite ? Evento.Favoravel : Evento.Desfavoravel;
            GestaoFase.EvoluiEstado(e, p);

            p.DateLimite = datalimite;
            p.CustoElegivel = custoElegivel;
            p.MontanteAprovado += montanteFinanciado;

            GestaoAccao.RegistaDespachoDecisaoIncentivos(p, despacho, aceite, custoElegivel, montanteFinanciado, datalimite);
            repositorio.AlteraProjecto(p);

            // Inicia timer... Projecto está em pagamento...
            temporizador.startTimer();
        }

        public void DespachoDecisaoBonificacoes(int idProjecto, string despacho, bool aceite, double taxaAprovada, double montanteMaximo, DateTime duracao)
        {
            Bonificacoes p = repositorio.ObterProjecto(idProjecto) as Bonificacoes;
            Evento e = aceite ? Evento.Favoravel : Evento.Desfavoravel;
            GestaoFase.EvoluiEstado(e, p);

            p.TaxaAprovada = taxaAprovada;
            p.DateLimite = duracao;
            p.MontanteMaximoDeFinanciamento = montanteMaximo;

            GestaoAccao.RegistaDespachoDecisaoBonificacoes(p, despacho, aceite, taxaAprovada, montanteMaximo, duracao);
            repositorio.AlteraProjecto(p);

            // Inicia timer... Projecto está em pagamento...
            temporizador.startTimer();
        }

        public void DespachoDecisaoTransformaBonificacao(int idProjecto, string despacho, double taxaAprovada, double montanteMaximo, DateTime duracao)
        {
            Projecto p = repositorio.ObterProjecto(idProjecto);
            Bonificacoes bonifica = Bonificacoes.FromProject(p);
            bonifica.TaxaAprovada = taxaAprovada;
            bonifica.DateLimite = duracao;
            bonifica.MontanteMaximoDeFinanciamento = montanteMaximo;

            GestaoAccao.RegistaDespachoDecisaoTransformaBonificacao(p, despacho, taxaAprovada, montanteMaximo, duracao);
            repositorio.AlteraProjecto(p);

            //Inicia timer... Projecto está em pagamento...
            temporizador.startTimer();
        }

        public bool EfectuaPagamento(int idProjecto, double montante)
        {
            Incentivos p = repositorio.ObterProjecto(idProjecto) as Incentivos;

            if (p == null)
                throw new InvalidOperationException("This operation cannot be called for Bonification projects");

            return GestaoPagamentos.EfectuaPagamento(p, montante);
        }

        public void PedeReforçoFinanciamento(int idProjecto, double montanteReforco)
        {
            Incentivos p = repositorio.ObterProjecto(idProjecto) as Incentivos;

            if (p == null)
                throw new InvalidOperationException("This operation cannot be called for Bonification projectos");

            p.Reforco += montanteReforco;

            GestaoAccao.RegistaPedidoReforço(p, montanteReforco);
            GestaoFase.EvoluiEstado(Evento.Reforçar, p);
            repositorio.AlteraProjecto(p);
        }

        public void SuspendeProjecto(int idProjecto)
        {
            Projecto p = repositorio.ObterProjecto(idProjecto);
            GestaoFase.EvoluiEstado(Evento.Suspender, p);
            GestaoAccao.RegistaSuspensao(p, p.EstadoAnterior);

            repositorio.AlteraProjecto(p);


            // parar de contar o tempo para o projecto acabar
            //não é necessário para o timer, da proxima vez que ele for executar
            // e nao existir projectos na fila, para automáticamente.
        }

        public void ReactivarProjecto(int idProjecto)
        {
            Projecto p = repositorio.ObterProjecto(idProjecto);
            GestaoFase.EvoluiEstado(Evento.Reactivar, p);
            GestaoAccao.RegistaReactivacao(p, p.EstadoActual);

            //Ajustar novamente o tempo do projecto
            DateTime dataSuspensao = p.Accoes.Where(a => a is Suspender).Last().DataHora;
            p.DateLimite += DateTime.Now - dataSuspensao;

            repositorio.AlteraProjecto(p);

            //iniciar contagem do tempo para o projecto acabar
            temporizador.startTimer();
        }

        public void AlterarDadosProjecto(Projecto p, Estados e)
        {
            GestaoAccao.RegistaAlteracaoDados(p, e);
            repositorio.AlteraProjecto(p);
        }
    }
}
